(function () {
const M = (DO.internal.app = DO.internal.app || {});
const leaveHooks = {};
DO.lifecycle = {
onLeave: function (viewId, fn) {
(leaveHooks[viewId] = leaveHooks[viewId] || []).push(fn);
},
run: function (viewId) {
const list = leaveHooks[viewId] || [];
leaveHooks[viewId] = [];
list.forEach(function (fn) {
try{ fn(); }catch (e) { DO.log.debug("core/app/lifecycle", e); }
});
}
};
// Roots emptied when the user leaves a page (null = the view container itself is the root).
const UNMOUNT = {
viewCategories: "categoriesRoot",
viewCategoryProducts: "catProductsRoot",
viewFlash: "flashRoot",
viewProduct: "productRoot",
viewWishlist: "wishlistRoot",
viewLegal: "legalRoot",
viewSuccess: "successRoot",
viewCheckout: null,
viewOrders: null,
viewAddresses: null,
viewEditProfile: null,
viewChangePassword: null,
viewLogin: null,
viewRegister: null,
viewAccount: null
};
// true only while that page is the one on screen (async work must check this before drawing)
M.viewActive = function (viewId) {
const v = document.getElementById(viewId);
return !!v && v.style.display !== "none";
};
const generation = {};
DO.lifecycle.guard = function (viewId) {
const mine = generation[viewId] || 0;
return function stale() {
return (generation[viewId] || 0) !== mine || !M.viewActive(viewId);
};
};
M.unmountView = function (viewId) {
if (!(viewId in UNMOUNT)) return;
generation[viewId] = (generation[viewId] || 0) + 1;
DO.lifecycle.run(viewId);
if (viewId === "viewCategoryProducts") M.cleanupCategoryIO();
const root = document.getElementById(UNMOUNT[viewId] || viewId);
if (!root) return;
if (viewId === "viewCategories") {
root.dataset.loaded = "";
root.dataset.catsOk = "0";
}
root.innerHTML = "";
};
function trackPageView(view, params){
try{
DO.app.trackEvent && DO.app.trackEvent("page_view", {
page_location: location.href,
page_path: location.pathname,
page_title: document.title || "Abyat Shop",
view: String(view || ""),
cat: params?.cat != null ? String(params.cat) : undefined,
product: params?.product != null ? String(params.product) : undefined,
q: params?.q != null ? String(params.q) : undefined
});
}catch (e) { DO.log.debug("core/app/trackPageView", e); }
}
M.ROUTES = {
home: {
viewId: "viewHome",
nav: "home",
enter: async () => {
await DO.pages.home.mount();
M.CartBadges.applyWhenCardsAppear(1500);
M.CartBadges.applyToVisible();
},
leave: () => {
DO.pages.home.unmount();
}
},
wishlist: {
viewId: "viewWishlist",
nav: "account",
enter: async () => {
if (!DO.state.wishlistLoaded) {
await M.loadScript("/do/assets/js/pages/wishlist.js");
DO.state.wishlistLoaded = true;
}
if (M.viewActive("viewWishlist") && typeof DO.pages.wishlist.render === "function") DO.pages.wishlist.render();
M.CartBadges.applyWhenCardsAppear(1200);
}
},
categories: {
viewId: "viewCategories",
nav: "categories",
enter: async () => {
const root = document.getElementById("categoriesRoot");
if (!root || typeof DO.pages.categories.load !== "function") {
M.CartBadges.applyWhenCardsAppear(1200);
return;
}
if (root.dataset.loaded !== "1") {
if (!M.__categoriesEnterPromise) {
M.__categoriesEnterPromise = (async () => {
await DO.pages.categories.load();
if (root.dataset.catsOk === "1") {
root.dataset.loaded = "1";
} else {
root.dataset.loaded = "";
}
})().finally(() => { M.__categoriesEnterPromise = null; });
}
await M.__categoriesEnterPromise;
}
M.CartBadges.applyWhenCardsAppear(1200);
}
},
category: {
viewId: "viewCategoryProducts",
nav: "categories",
enter: async (r) => {
if (r.cat) await DO.app.showCategoryProducts(r.cat, false, r.seo_url || r.seo || "");
}
},
product: {
viewId: "viewProduct",
nav: "categories",
enter: async (r) => {
if (!r.product) return;
M.cleanupCategoryIO();
// منطلق ViewContent فورًا من product_id الموجود أصلاً بالرابط، قبل ما ننتظر
// بيانات المنتج الكاملة توصل من الـ API. فيسبوك محتاج بس content_ids لهالحدث،
// فما في داعي نأخر إرساله لحد ما تخلص صفحة المنتج تحميلها بالكامل — تأخير كان
// عم يخسرنا أحداث حقيقية لما الزائر يطلع بسرعة (خصوصًا جايين من إعلان).
try{
DO.app.trackEvent && DO.app.trackEvent("view_item", {
items: [{ item_id: String(r.product) }],
page_location: location.href,
page_path: location.pathname
});
}catch (e) { DO.log.debug("core/app/enter", e); }
if (typeof DO.product.show === "function") {
await DO.product.show(String(r.product), false);
} else {
console.warn("showProduct غير موجودة بعد");
}
M.CartBadges.applyWhenCardsAppear(900);
}
},
search_home: {
viewId: "viewSearch",
nav: "categories",
enter: async () => {
if (!DO.state.searchLoaded) {
await M.loadScripts(["/do/assets/js/pages/search/helpers.js","/do/assets/js/pages/search/recent.js","/do/assets/js/pages/search/shimmer.js","/do/assets/js/pages/search/scaffold.js","/do/assets/js/pages/search/api.js","/do/assets/js/pages/search/trending.js","/do/assets/js/pages/search/page.js"]);
DO.state.searchLoaded = true;
}
DO.pages.search.cleanupIO?.();
if (typeof DO.pages.search.renderHome === "function") {
await DO.pages.search.renderHome({ limit: 12, days: 30 });
} else {
const root = document.getElementById("viewSearch");
if (root){
root.innerHTML = `
`;
}
}
M.CartBadges.applyWhenCardsAppear(900);
M.CartBadges.applyToVisible();
}
},
search: {
viewId: "viewSearch",
nav: "categories",
enter: async (r) => {
const q = String(r.q || "").trim();
if (!q) return;
if (!DO.state.searchLoaded) {
await M.loadScripts(["/do/assets/js/pages/search/helpers.js","/do/assets/js/pages/search/recent.js","/do/assets/js/pages/search/shimmer.js","/do/assets/js/pages/search/scaffold.js","/do/assets/js/pages/search/api.js","/do/assets/js/pages/search/trending.js","/do/assets/js/pages/search/page.js"]);
DO.state.searchLoaded = true;
}
let mount = document.getElementById("searchResultsRoot");
if (!mount) {
let root = document.getElementById("searchRoot");
if (!root) {
root = document.createElement("div");
root.id = "searchRoot";
const view = document.getElementById("viewSearch");
if (view) view.appendChild(root);
}
mount = document.createElement("div");
mount.id = "searchResultsRoot";
root.appendChild(mount);
}
try{
DO.app.trackEvent && DO.app.trackEvent("search", {
search_term: q, q,
page_location: location.href,
page_path: location.pathname
});
}catch (e) { DO.log.debug("core/app/enter", e); }
DO.pages.search.cleanupIO?.();
await DO.pages.search.show(q, false, { mountId: "searchResultsRoot" });
M.CartBadges.applyWhenCardsAppear(1200);
M.CartBadges.applyToVisible();
}
},
cart: {
viewId: "viewCart",
nav: "cart",
enter: async () => {
if (typeof DO.pages.cart.load === "function") {
await DO.pages.cart.load();
} else if (typeof window.loadCart === "function") {
await window.loadCart();
} else {
console.warn("cart.js مش عامل loadCartUI أو loadCart");
}
await M.CartBadges.sync(true);
},
leave: () => {
if (typeof DO.pages.cart.unmount === "function") DO.pages.cart.unmount();
}
},
login: {
viewId: "viewLogin",
nav: "account",
enter: async () => {
if (!DO.state.loginLoaded) {
await M.loadScript("/do/assets/js/pages/account/login.js");
DO.state.loginLoaded = true;
}
M.viewActive("viewLogin") && DO.pages.login.render && DO.pages.login.render();
}
},
register: {
viewId: "viewRegister",
nav: "account",
enter: async () => {
if (!DO.state.registerLoaded) {
await M.loadScript("/do/assets/js/pages/account/register.js");
DO.state.registerLoaded = true;
}
M.viewActive("viewRegister") && DO.pages.register.render && DO.pages.register.render();
}
},
return: {
viewId: "viewLegal",
nav: "account",
enter: async () => {
await M.loadCss("/do/assets/css/pages/legal.css");
await M.loadScript("/do/assets/js/pages/legal.js");
M.viewActive("viewLegal") && DO.pages.legal.render && DO.pages.legal.render("return");
}
},
privacy: {
viewId: "viewLegal",
nav: "account",
enter: async () => {
await M.loadCss("/do/assets/css/pages/legal.css");
await M.loadScript("/do/assets/js/pages/legal.js");
M.viewActive("viewLegal") && DO.pages.legal.render && DO.pages.legal.render("privacy");
}
},
terms: {
viewId: "viewLegal",
nav: "account",
enter: async () => {
await M.loadCss("/do/assets/css/pages/legal.css");
await M.loadScript("/do/assets/js/pages/legal.js");
M.viewActive("viewLegal") && DO.pages.legal.render && DO.pages.legal.render("terms");
}
},
checkout: {
viewId: "viewCheckout",
nav: "cart",
enter: async () => {
if (!DO.state.checkoutLoaded) {
await M.loadScripts(["/do/assets/js/pages/checkout/helpers.js","/do/assets/js/pages/checkout/tracking.js","/do/assets/js/pages/checkout/api.js","/do/assets/js/pages/checkout/skeleton.js","/do/assets/js/pages/checkout/address.js","/do/assets/js/pages/checkout/summary.js","/do/assets/js/pages/checkout/picker.js","/do/assets/js/pages/checkout/page.js"]);
await M.loadCss("/do/assets/css/pages/checkout.css");
DO.state.checkoutLoaded = true;
}
M.viewActive("viewCheckout") && DO.pages.checkout.render && DO.pages.checkout.render();
}
},
orders: {
viewId: "viewOrders",
nav: "account",
enter: async () => {
if (!DO.state.ordersLoaded) {
await M.loadScript("/do/assets/js/pages/account/orders.js");
DO.state.ordersLoaded = true;
}
M.viewActive("viewOrders") && DO.pages.orders.render && DO.pages.orders.render();
}
},
addresses: {
viewId: "viewAddresses",
nav: "account",
enter: async () => {
if (!DO.state.addressesLoaded) {
await M.loadScript("/do/assets/js/pages/account/addresses.js");
DO.state.addressesLoaded = true;
}
M.viewActive("viewAddresses") && DO.pages.addresses.render && DO.pages.addresses.render();
}
},
edit_profile: {
viewId: "viewEditProfile",
nav: "account",
enter: async () => {
if (!DO.state.editProfileLoaded) {
await M.loadScript("/do/assets/js/pages/account/edit-profile.js");
DO.state.editProfileLoaded = true;
}
M.viewActive("viewEditProfile") && DO.pages.editProfile.render && DO.pages.editProfile.render();
}
},
change_password: {
viewId: "viewChangePassword",
nav: "account",
enter: async () => {
if (!DO.state.changePasswordLoaded) {
await M.loadScript("/do/assets/js/pages/account/change-password.js");
DO.state.changePasswordLoaded = true;
}
M.viewActive("viewChangePassword") && DO.pages.changePassword.render && DO.pages.changePassword.render();
}
},
flash: {
viewId: "viewFlash",
nav: "flash",
enter: async () => {
if (!DO.state.flashLoaded) {
await M.loadScripts(["/do/assets/js/pages/flash/helpers.js","/do/assets/js/pages/flash/styles.js","/do/assets/js/pages/flash/api.js","/do/assets/js/pages/flash/modules.js","/do/assets/js/pages/flash/slider.js","/do/assets/js/pages/flash/page.js"]);
DO.state.flashLoaded = true;
}
if (M.viewActive("viewFlash") && typeof DO.pages.flash.render === "function") DO.pages.flash.render();
}
},
success: {
viewId: "viewSuccess",
nav: "cart",
enter: async () => {
if (!DO.state.successLoaded){
await M.loadCss("/do/assets/css/pages/success.css");
await M.loadScript("/do/assets/js/pages/success.js");
DO.state.successLoaded = true;
}
M.viewActive("viewSuccess") && DO.pages.success.render && DO.pages.success.render();
}
},
account: {
viewId: "viewAccount",
nav: "account",
enter: async () => {
M.viewActive("viewAccount") && DO.pages.account.render && DO.pages.account.render();
}
},
};
// Bumped at the start of every go() call. If a SECOND call starts and finishes while an EARLIER one is
// still waiting (e.g. on DO.css.forRoute, or the SEO lookup below), the earlier one must not go on to
// call route.enter() once its wait ends — otherwise a slow first navigation can resume after a faster
// second one already finished, and silently overwrite whatever the user is now actually looking at.
// This is a real race (confirmed: rapid back-and-forth between two products within ~2s triggers it),
// not specific to any one page type, so the guard lives here rather than duplicated per route.
let navGen = 0;
async function go(view, params={}, push=true){
const myNavGen = ++navGen;
const stale = () => myNavGen !== navGen;
const r = { view, ...params };
if (push){
if (view === "home") M.setPath("/");
else if (view === "categories") M.setPath("/categories");
else if (view === "cart") M.setPath("/cart");
else if (view === "account") M.setPath("/account");
else if (view === "privacy") M.setPath("/privacy");
else if (view === "terms") M.setPath("/terms");
else if (view === "return") M.setPath("/return");
else if (view === "flash") M.setPath("/flash");
else if (view === "wishlist") M.setPath("/wishlist");
else if (view === "login") M.setPath("/login");
else if (view === "register") M.setPath("/register");
else if (view === "orders") M.setPath("/orders");
else if (view === "addresses") M.setPath("/addresses");
else if (view === "edit_profile") M.setPath("/edit-profile");
else if (view === "change_password") M.setPath("/change-password");
else if (view === "checkout") M.setPath("/checkout");
else if (view === "success") M.setPath("/success");
else if (view === "search") {
const q = String(params.q || "").trim();
if (q) M.setPath("/search/" + M.encodeSeg(q));
else M.setPath("/search");
}
else if (view === "search_home") {
M.setPath("/search");
}
else if (view === "category") {
let seoPath = params.seo_url || params.seo || "";
if (!seoPath && params.cat != null){
try{
const rr = await DO.api.fetch(`/index.php?route=extension/mstore/seo&category_id=${encodeURIComponent(params.cat)}`);
if (stale()) return; // a newer navigation has already taken over; don't set the URL for this one
seoPath = rr?.seo_url || (rr?.keyword ? ("/" + String(rr.keyword).replace(/^\/+/, "")) : "");
}catch (e) { DO.log.warn("core/app/go", e); }
}
if (seoPath && String(seoPath).startsWith("/")) M.setPath(String(seoPath));
else if (seoPath) M.setPath("/" + String(seoPath).replace(/^\/+/, ""));
else M.setPath("/category/" + M.encodeSeg(params.cat || ""));
}
else if (view === "product"){
let seoPath = params.seo_url || params.seo || "";
if (!seoPath && params.product != null){
try{
const rr = await DO.api.fetch(`/index.php?route=extension/mstore/seo&product_id=${encodeURIComponent(params.product)}`);
if (stale()) return; // a newer navigation has already taken over; don't set the URL for this one
seoPath = rr?.seo_url || (rr?.keyword ? ("/" + String(rr.keyword).replace(/^\/+/, "")) : "");
}catch (e) { DO.log.warn("core/app/go", e); }
}
if (seoPath && String(seoPath).startsWith("/")) M.setPath(String(seoPath));
else if (seoPath) M.setPath("/" + String(seoPath).replace(/^\/+/, ""));
else M.setPath("/product/" + M.encodeSeg(params.product || ""));
}
else M.setPath("/");
}
const route = M.ROUTES[view] || M.ROUTES.home;
// Switch away from the page being left BEFORE awaiting anything (CSS for the new route can take a
// moment, even from cache). Otherwise the OLD page stays fully visible for that whole wait, which on a
// real device reads as "it shows the page I'm leaving for a moment" before the new one appears.
const prevView = DO.state.DO_ROUTE && DO.state.DO_ROUTE.view;
if (prevView && prevView !== view) {
const prev = M.ROUTES[prevView];
if (prev && typeof prev.leave === "function") {
try{
prev.leave();
}catch (e) { DO.log.warn("core/app/leave", e); }
}
if (prev && prev.viewId !== route.viewId) M.unmountView(prev.viewId);
}
M.showView(route.viewId);
M.setActiveNav(route.nav);
M.setBodyViewClass(view);
// Product-to-product is a same-view transition (view stays "product" both times), so the generic
// leave/unmount block above never runs for it. Without this, the OLD product's fully rendered page
// stays fully visible — not just briefly, but for as long as the CSS-loading await below takes — until
// showProductImpl's own renderSkeleton() finally clears it, which only happens afterward. Clearing it
// to a skeleton right here, before that wait, is what actually fixes the "shows the page I'm leaving"
// flash for Back/Next between two products specifically.
if (view === "product" && prevView === "product"){
try{ DO.internal.product.renderSkeleton?.(document.getElementById("productRoot")); }
catch (e) { DO.log.debug("core/app/go", e); }
}
await DO.css.forRoute(view);
// The most important check: this wait is exactly where a slow navigation can lag behind a faster,
// later one. If a newer go() has started since, that one already showed its own view and will finish
// on its own — this older call must stop here rather than go on to call route.enter() and overwrite
// whatever the user is now actually looking at with stale data for the page they already left.
if (stale()) return;
if (push) {
M.Scroll.resetTop();
M.Scroll.settleTop(1200);
}
DO.state.DO_ROUTE = Object.assign({}, r, {
view: view,
cat: (view === "category" ? String(r.cat || "") : undefined),
product: (view === "product" ? String(r.product || "") : undefined),
q: (view === "search" ? String(r.q || "") : undefined)
});
try{
window.dispatchEvent(new CustomEvent("do:route", { detail: DO.state.DO_ROUTE }));
}catch (e) { DO.log.debug("core/app/go", e); }
trackPageView(view, DO.state.DO_ROUTE);
await route.enter(r);
M.CartBadges.applyToVisible();
M.CartBadges.applyWhenCardsAppear(900);
}
async function handleRoute(){
M.cleanupCategoryIO();
const parsed = M.parseRouteFromPath();
if (parsed.kind === "seo"){
if (parsed.slug === "success") return go("success", {}, false);
if (parsed.slug === "privacy") return go("privacy", {}, false);
if (parsed.slug === "terms") return go("terms", {}, false);
if (parsed.slug === "return") return go("return", {}, false);
if (parsed.slug === "cart") return go("cart", {}, false);
if (parsed.slug === "account") return go("account", {}, false);
if (parsed.slug === "categories") return go("categories", {}, false);
if (parsed.slug === "flash") return go("flash", {}, false);
if (parsed.slug === "wishlist") return go("wishlist", {}, false);
if (parsed.slug === "checkout") return go("checkout", {}, false);
if (parsed.slug === "login") return go("login", {}, false);
if (parsed.slug === "register") return go("register", {}, false);
if (parsed.slug === "orders") return go("orders", {}, false);
if (parsed.slug === "addresses") return go("addresses", {}, false);
if (parsed.slug === "edit-profile") return go("edit_profile", {}, false);
if (parsed.slug === "change-password") return go("change_password", {}, false);
if (parsed.slug.startsWith("search/")){
const q = M.decodeSeg(parsed.slug.split("/").slice(1).join("/"));
return go("search", { q }, false);
}
if (parsed.slug.startsWith("category/")){
const id = M.decodeSeg(parsed.slug.split("/").slice(1).join("/"));
return go("category", { cat:id }, false);
}
if (parsed.slug.startsWith("product/")){
const id = M.decodeSeg(parsed.slug.split("/").slice(1).join("/"));
return go("product", { product:id }, false);
}
const seo = await M.resolveSeoSlugToTarget(parsed.slug);
if (seo){
if (seo.type === "product"){
return go("product", { product: String(seo.id), seo: "/" + parsed.slug }, false);
}
if (seo.type === "category"){
const seoUrl = "/" + parsed.slug;
M.CatSEO.set(String(seo.id), seoUrl);
return go("category", { cat: String(seo.id), seo_url: seoUrl, path: String(seo.path || "") }, false);
}
}
return go("home", {}, false);
}
const v = parsed.view;
const p = parsed.params || {};
if (v === "home") return go("home", {}, false);
if (v === "categories") return go("categories", {}, false);
if (v === "cart") return go("cart", {}, false);
if (v === "account") return go("account", {}, false);
if (v === "flash") return go("flash", {}, false);
if (v === "wishlist") return go("wishlist", {}, false);
if (v === "login") return go("login", {}, false);
if (v === "register") return go("register", {}, false);
if (v === "orders") return go("orders", {}, false);
if (v === "addresses") return go("addresses", {}, false);
if (v === "edit_profile") return go("edit_profile", {}, false);
if (v === "change_password") return go("change_password", {}, false);
if (v === "checkout") return go("checkout", {}, false);
if (v === "privacy") return go("privacy", {}, false);
if (v === "return") return go("return", {}, false);
if (v === "terms") return go("terms", {}, false);
if (v === "success") return go("success", {}, false);
if (v === "search_home") return go("search_home", {}, false);
if (v === "search") return go("search", { q: p.q || "" }, false);
if (v === "category") return go("category", { cat: p.cat || "" }, false);
if (v === "product") return go("product", { product: p.product || "" }, false);
return go("home", {}, false);
}
async function openAccountEntry(){
try{
const me = await DO.api.fetch("account");
if (me && (me.success === 1 || me.success === true) && me.data && me.data.customer_id) {
return go("account", {}, true);
}
}catch (e) { DO.log.warn("core/app/openAccountEntry", e); }
try{
const t = localStorage.getItem("do_refresh_token");
if (t) {
const rr = await DO.api.fetch("account/refresh", { method:"POST", data:{ refresh_token: t }});
if (rr && (rr.success === 1 || rr.success === true)) {
return go("account", {}, true);
}
}
}catch (e) { DO.log.warn("core/app/openAccountEntry", e); }
if (!DO.authModal.open) {
await M.loadScripts(["/do/assets/js/components/auth-modal/helpers.js","/do/assets/js/components/auth-modal/phone-google.js","/do/assets/js/components/auth-modal/modal.js"]);
await M.loadCss("/do/assets/css/components/auth-modal.css");
}
DO.authModal.open("chooser");
}
M.go = go;
M.handleRoute = handleRoute;
M.openAccountEntry = openAccountEntry;
})();
Fatal error: Uncaught Error: Class 'Controllerstartuprouter' not found in /home/u996230478/domains/abyatshop.com/public_html/system/storageipsugq8vr1oe/modification/system/engine/action.php:71
Stack trace:
#0 /home/u996230478/domains/abyatshop.com/public_html/system/engine/router.php(67): Action->execute(Object(Registry))
#1 /home/u996230478/domains/abyatshop.com/public_html/system/engine/router.php(56): Router->execute(Object(Action))
#2 /home/u996230478/domains/abyatshop.com/public_html/system/framework.php(165): Router->dispatch(Object(Action), Object(Action))
#3 /home/u996230478/domains/abyatshop.com/public_html/system/startup.php(104): require_once('/home/u99623047...')
#4 /home/u996230478/domains/abyatshop.com/public_html/index.php(24): start('catalog')
#5 {main}
thrown in /home/u996230478/domains/abyatshop.com/public_html/system/storageipsugq8vr1oe/modification/system/engine/action.php on line 71